home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
prog
/
atari
/
c
/
nos042_s
/
smtpserv.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-16
|
34KB
|
1,553 lines
/* SMTP Server state machine - see RFC 821
* enhanced 4/88 Dave Trulli nn2z
*/
/****************************************************************************
* $Id: smtpserv.c 1.13 94/01/04 14:10:28 ROOT_DOS Exp $
*
* 25 Jun 92 paul@wolf.demon.co.uk added automated mail bouncing
* 13 Jul 92 1.3 GT Remove 1.2 changes.
* 27 Aug 92 1.6 mt@kram.demon.co.uk added smtp separator
* 03 Sep 92 1.7 GT Fix missing BOUNCER conditional.
* 08 Dec 92 1.8 mt@kram.org: Occasional pwait() during copy
* cms@home: Alias-File Mail Bouncing System
* 19 Mar 93 1.9 GT Debugging mailit.
* 04 Apr 93 1.10 GT Re-enable beep.
* 05 Apr 93 1.11 GT Reinstate delay call.
* 08 May 93 1.12 GT Fix warnings.
* IAY Improve behaviour of rest of system
* while this module is copying files,
* particularly while in SMTP MODE QUEUE.
* IAY Fix dot transparency as per RFC 821.
* GBD Fix alias expansion to allow multiple spaces.
* 08 Dec 93 1.13 GT Fix bounce message envelope From.
* Version and compilation date in SMTP banner.
* Implement VRFY.
* Fix wandering pointer in getmsgtxt ().
* Close mail spool file before ACK.
* Fix "." at column 256.
*
* ATARI Version by David Nash - dnash@chaos.demon.co.uk
*
* __stdargs smtpserv
*
****************************************************************************/
/*
** The following definition controls how often the copy_data
** function calls pwait(). Lower numbers mean better response
** to other things that may be going on at the expense of the
** speed of that particular function.
*/
#define COPY_DATA_WAIT 1
#include <stdio.h>
#include <time.h>
#undef DEBUG_LOCKS
#ifdef NOVELL
#include "nit.h"
#include <io.h>
#include <dos.h>
#include <dir.h>
#include <time.h>
#endif
extern char novell_server_name[128];
extern char novell_mail_ext[4];
extern unsigned short novell_start;
extern char *smtp_separator;
extern int Smtpbeep;
#ifdef UNIX
#include <sys/types.h>
#endif
#if defined(__STDC__) || defined(__TURBOC__)
#include <stdarg.h>
#endif
#include <ctype.h>
#include <setjmp.h>
#include "global.h"
#include "mbuf.h"
#include "cmdparse.h"
#include "socket.h"
#include "iface.h"
#include "proc.h"
#include "smtp.h"
#include "commands.h"
#include "dirutil.h"
#include "bm.h"
#include "mailbox.h"
#include "domain.h"
#include "files.h"
#include "ip.h"
char *Days[7] =
{
"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
}
;
char *Months[12] =
{
"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"
}
;
static void copy_data __ARGS((FILE *from, FILE *to));
static struct list *expandalias __ARGS((struct list **head,char *user));
static int getmsgtxt __ARGS((struct smtpsv *mp));
static struct smtpsv *mail_create __ARGS((void));
static void mail_clean __ARGS((struct smtpsv *mp));
static int mailit __ARGS((FILE *data,char *from,struct list *tolist));
static int router_queue __ARGS((FILE *data,char *from,struct list *to));
static void smtplog __ARGS((char *fmt,...));
static void __stdargs smtpserv __ARGS((int s,void *unused,void *p));
static int mailuser __ARGS((FILE *data,char *from,char *to));
static int validate_user __ARGS((char *user));
static int validate_sender __ARGS((char *sender));
static int remlist __ARGS((struct list **head, struct list *unwanted));
/* Command table */
static char *commands[] =
{
"helo",
#define HELO_CMD 0
"noop",
#define NOOP_CMD 1
"mail from:",
#define MAIL_CMD 2
"quit",
#define QUIT_CMD 3
"rcpt to:",
#define RCPT_CMD 4
"help",
#define HELP_CMD 5
"data",
#define DATA_CMD 6
"rset",
#define RSET_CMD 7
"expn",
#define EXPN_CMD 8
"vrfy",
#define VRFY_CMD 9
NULLCHAR
}
;
/* Reply messages */
static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
static char Banner[] = "220 %s %s %s %s SMTP ready\n";
static char Closing[] = "221 Closing\n";
static char Ok[] = "250 Ok\n";
static char Reset[] = "250 Reset state\n";
static char Sent[] = "250 Sent\n";
static char Ourname[] = "250 %s, Pleased to meet you\n";
static char Unwanted[] = "250 Local policy blocks mail from <%s>\n";
static char Enter[] = "354 Enter mail, end with .\n";
static char Ioerr[] = "452 Temp file write error\n";
static char Badcmd[] = "500 Command unrecognized\n";
static char Lowmem[] = "421 System overloaded, try again later\n";
static char Syntax[] = "501 Syntax error\n";
static char Needrcpt[] = "503 Need RCPT (recipient)\n";
static char Needsender[] = "503 Local policy blocking mail from you\n";
static char Unknown[] = "550 <%s> address unknown\n";
static char Noalias[] = "550 No alias for <%s>\n";
static char UnknownRcpt[] = "550 Unknown recipient <%s>\n";
static failure fail;
static int bad_sender;
char default_address[SLINELEN];
static int Ssmtp = -1; /* prototype socket for service */
/* Start up SMTP receiver service */
int
smtp1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct sockaddr_in lsocket;
int s;
if(Ssmtp != -1)
{
return 0;
}
psignal(Curproc,0); /* Don't keep the parser waiting */
chname(Curproc,"SMTP listener");
lsocket.sin_family = AF_INET;
lsocket.sin_addr.s_addr = INADDR_ANY;
if(argc < 2)
lsocket.sin_port = IPPORT_SMTP;
else
lsocket.sin_port = atoi(argv[1]);
Ssmtp = socket(AF_INET,SOCK_STREAM,0);
bind(Ssmtp,(char *)&lsocket,sizeof(lsocket));
listen(Ssmtp,1);
for(;;)
{
if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1)
break; /* Service is shutting down */
if(availmem() < Memthresh)
{
usprintf(s,Lowmem);
shutdown(s,1);
}
else
{
/* Spawn a server */
newproc("SMTP server",2048,smtpserv,s,NULL,NULL,0);
}
}
return 0;
}
/* Shutdown SMTP service (existing connections are allowed to finish) */
int
smtp0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
close_s(Ssmtp);
Ssmtp = -1;
return 0;
}
static void __stdargs smtpserv(s,unused,p)
int s;
void *unused;
void *p;
{
struct smtpsv *mp;
char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr;
struct list *ap,*list;
int cnt;
char address_type;
sockmode(s,SOCK_ASCII);
sockowner(s,Curproc); /* We own it now */
log(s,"open SMTP");
if((mp = mail_create()) == NULLSMTPSV)
{
tprintf(Nospace);
log(s,"close SMTP - no space");
close_s(s);
return;
}
mp->s = s;
(void) usprintf(s, Banner, Hostname, Version, __DATE__, __TIME__);
loop:
if ((cnt = recvline(s,buf,sizeof(buf))) == -1)
{
/* He closed on us */
goto quit;
}
if(cnt < 4)
{
/* Can't be a legal command */
usprintf(mp->s,Badcmd);
goto loop;
}
rip(buf);
cmd = buf;
/* Translate entire buffer to lower case */
for(cp = cmd;*cp != '\0';cp++)
*cp = tolower(*cp);
/* Find command in table; if not present, return syntax error */
for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
break;
if(*cmdp == NULLCHAR)
{
(void) usprintf(mp->s,Badcmd);
goto loop;
}
arg = &cmd[strlen(*cmdp)];
/* Skip spaces after command */
while(*arg == ' ')
arg++;
/* Execute specific command */
switch(cmdp-commands) {
case HELO_CMD:
free(mp->system);
mp->system = strdup(arg);
(void) usprintf(mp->s,Ourname,Hostname);
break;
case NOOP_CMD:
(void) usprintf(mp->s,Ok);
break;
case MAIL_CMD:
bad_sender = 0;
if((cp = getname(arg)) == NULLCHAR)
{
(void) usprintf(mp->s,Syntax);
break;
}
if (!validate_sender(cp))
{
(void) usprintf(mp->s,Unwanted,cp);
smtplog("rejected: from: %s",cp);
bad_sender = 1;
break;
}
free(mp->from);
mp->from = strdup(cp);
(void) usprintf(mp->s,Ok);
break;
case QUIT_CMD:
(void) usprintf(mp->s,Closing);
goto quit;
case RCPT_CMD:
/* Specify recipient */
fail = NO_FAIL;
if((cp = getname(arg)) == NULLCHAR)
{
(void) usprintf(mp->s,Syntax);
break;
}
/* rewrite address if possible */
if((newaddr = rewrite_address(cp)) != NULLCHAR)
{
strcpy(buf,newaddr);
cp = buf;
free(newaddr);
}
/* check if address is ok */
if ((address_type = validate_address(cp)) == BADADDR)
{
(void) usprintf(mp->s,Unknown,cp);
break;
}
/* if a local address check for an alias */
if (address_type == LOCAL)
{
expandalias(&mp->to, cp);
for (ap = mp->to; ap != NULLLIST; ap = ap->next)
{
if (ap->type == LOCAL)
{
if (!validate_user(ap->val))
{
switch (fail)
{
case FAIL_DELIVER:
break;
case FAIL_DEFAULT:
expandalias(&ap->next, default_address);
if (validate_user(ap->next->val))
{
remlist(&mp->to, ap);
break;
}
/* else fall through to fail */
default:
usprintf(mp->s,UnknownRcpt,ap->val);
fail = FAIL_BAD;
break;
}
if (fail == FAIL_BAD)
break;
}
}
}
if (fail == FAIL_BAD)
break;
}
else
/* a remote address is added to the list */
addlist(&mp->to, cp, address_type);
(void) usprintf(mp->s,Ok);
break;
case HELP_CMD:
(void) usprintf(mp->s,Help);
break;
case DATA_CMD:
if ((mp->to == NULLLIST) || fail == FAIL_BAD)
(void) usprintf(mp->s,Needrcpt);
else if (bad_sender)
(void) usprintf(mp->s,Needsender);
else if ((mp->data = tmpfile()) == NULLFILE)
(void) usprintf(mp->s,Ioerr);
else
getmsgtxt(mp);
break;
case RSET_CMD:
del_list(mp->to);
mp->to = NULLLIST;
fail = NO_FAIL;
(void) usprintf(mp->s,Reset);
break;
case EXPN_CMD:
if (*arg == '\0')
{
(void) usprintf(mp->s,Syntax);
break;
}
list = NULLLIST;
/* rewrite address if possible */
if((newaddr = rewrite_address(arg)) != NULLCHAR)
if(strcmp(newaddr,arg) == 0)
{
free(newaddr);
newaddr = NULLCHAR;
}
else
{
strcpy(buf,newaddr);
arg = buf;
}
list = NULLLIST;
expandalias(&list,arg);
if (strcmp(list->val,arg) == 0 && list->next == NULLLIST)
if(newaddr == NULLCHAR)
{
(void) usprintf(mp->s,Noalias,arg);
del_list(list);
break;
}
ap = list;
while (ap->next != NULLLIST)
{
(void) usprintf(mp->s,"250-%s\n",ap->val);
ap = ap->next;
}
usprintf(mp->s,"250 %s\n",ap->val);
del_list(list);
free(newaddr);
break;
case VRFY_CMD:
if (*arg == '\0') {
usprintf(mp->s, Syntax);
break;
}
if (validate_user(arg) == 0)
usprintf(mp->s, UnknownRcpt, arg);
else
usprintf(mp->s, Ok);
break;
}
goto loop;
quit:
log(mp->s,"close SMTP");
close_s(mp->s);
mail_clean(mp);
smtptick(NULL); /* start SMTP daemon immediately */
}
/* read the message text */
static int
getmsgtxt(mp)
struct smtpsv *mp;
{
char buf[LINELEN];
register char *p = buf;
long t;
/* Add timestamp; ptime adds newline */
time(&t);
fprintf(mp->data,"Received: ");
if (mp->system != NULLCHAR)
fprintf(mp->data, "from %s ", mp->system);
fprintf(mp->data, "by %s with SMTP\n\tid AA%ld ; %s",
Hostname, get_msgid(), ptime(&t));
if (ferror(mp->data)) {
usprintf(mp->s, Ioerr);
return 1;
}
else
usprintf(mp->s, Enter);
while(1) {
int no_check_dot = 0; /* don't check for "." */
char *has_newline; /* nz - line contains newline */
p = buf; /* GT 11 Dec 93 */
if (recvline(mp->s, p, sizeof(buf)) == -1)
return 1;
has_newline = strchr(p, '\n');
rip(p);
/*
** If the line starts with a '.', this is either
** the end of the message or a line where a dot
** has been added for "dot transparency"
*/
if (no_check_dot == 0 && *p == '.') {
/*
** Strip off leading '.'; if there is nothing
** else on the line, this is the end of the
** data. Otherwise, we have managed to remove
** the protecting dot and p now points at the
** unprotected message line.
*/
if (*++p == '\0') {
int rc; /* result code */
/* Also sends appropriate response */
rc = mailit(mp->data, mp->from, mp->to);
if (fclose(mp->data) != 0)
rc = 1;
mp->data = NULLFILE;
if (rc != 0)
(void) usprintf(mp->s,Ioerr);
else
(void) usprintf(mp->s,Sent);
del_list(mp->to);
mp->to = NULLLIST;
return 0;
}
}
if (has_newline != 0)
no_check_dot = 0; /* check "." next time */
else
no_check_dot = 1; /* don't check "." next time */
/* for UNIX mail compatiblity */
if (strncmp(p,"From ",5) == 0)
(void) putc('>',mp->data);
/* Append to data file */
if(fprintf(mp->data,"%s\n",p) < 0) {
(void) usprintf(mp->s,Ioerr);
return 1;
}
}
}
/* Create control block, initialize */
static struct smtpsv *
mail_create()
{
register struct smtpsv *mp;
mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv));
mp->from = strdup(""); /* Default to null From address */
return mp;
}
/* Free resources, delete control block */
static void
mail_clean(mp)
register struct smtpsv *mp;
{
if (mp == NULLSMTPSV)
return;
free(mp->system);
free(mp->from);
if(mp->data != NULLFILE)
fclose(mp->data);
del_list(mp->to);
free((char *)mp);
}
/* Given a string of the form <user@host>, extract the part inside the
* brackets and return a pointer to it.
*/
char *
getname(cp)
register char *cp;
{
register char *cp1;
if ((cp = strchr(cp,'<')) == NULLCHAR)
return NULLCHAR;
cp++; /* cp -> first char of name */
if ((cp1 = strchr(cp,'>')) == NULLCHAR)
return NULLCHAR;
*cp1 = '\0';
return cp;
}
#ifdef NOVELL
long int get_mail_id(char *user,char *server)
{
int connectionID=1;
int cCode=0;
int DefCon;
long int retval;
int err;
cCode=GetConnectionID(server,&connectionID);
DefCon=GetDefaultConnectionID();
if(DefCon!=connectionID)
{
SetPreferredConnectionID(connectionID);
}
cCode=GetBinderyObjectID(user,OT_USER,&retval);
if(cCode!=0)
{
retval=0;
}
if(DefCon!=connectionID)
{
SetPrimaryConnectionID(DefCon);
}
return retval;
}
void send_alert(char *user,char *server,char *from)
{
int numcon,conlist[100],reslist[100];
int connectionID=1;
int cCode=0;
int DefCon;
long int retval;
int err;
char message[128];
cCode=GetConnectionID(server,&connectionID);
DefCon=GetDefaultConnectionID();
if(DefCon!=connectionID)
{
SetPreferredConnectionID(connectionID);
}
cCode=GetObjectConnectionNumbers(user,OT_USER,&numcon,conlist,100);
sprintf(message,"You have mail from %s",from);
message[55]=0; /* Nasty truncation to cope with bindings */
cCode=SendBroadcastMessage(message,conlist,reslist,numcon);
if(DefCon!=connectionID)
{
SetPrimaryConnectionID(DefCon);
}
}
#endif
/*
** copy_data
**
** This function copies all available data from an input file to an
** output file. This is done in chunks of LINELEN for efficiency.
** In addition, the function deschedules itself every so often so
** that large copy operations of this type do not completely block
** processing of other operations.
*/
static void
copy_data ( from, to )
FILE *from, *to;
{
int c;
#if COPY_DATA_WAIT != 1
int counter = 0;
#endif
char buf[LINELEN];
while ( (c = fread(buf, 1, sizeof(buf), from) ) > 0)
{
if (fwrite(buf, 1, c, to) != c)
break;
#if COPY_DATA_WAIT == 1
pwait(NULL);
#else
if (++counter == COPY_DATA_WAIT)
{
pwait(NULL);
counter = 0;
}
#endif
}
}
/* General mailit function. It takes a list of addresses which have already
** been verified and expanded for aliases. Base on the current mode the message
** is place in an mbox, the outbound smtp queue or the rqueue interface
*/
static int
mailit(data,from,tolist)
FILE *data;
char *from;
struct list *tolist;
{
struct list *ap, *dlist = NULLLIST;
register FILE *fp;
char mailbox[85], *cp, *host, *qhost;
int fail = 0;
time_t t;
if ((Smtpmode & QUEUE) != 0)
return(router_queue(data,from,tolist));
do
{
qhost = NULLCHAR;
for(ap = tolist;ap != NULLLIST;ap = ap->next)
{
#if defined (DEBUG_LOCKS)
smtplog ("mailit (): ap->val = %s", ap->val);
smtplog ("mailit (): ap->type = %d", ap->type);
#endif
if (ap->type == DOMAIN)
{
if ((host = strrchr(ap->val,'@')) != NULLCHAR)
host++;
else
host = Hostname;
if(qhost == NULLCHAR)
qhost = host;
if(stricmp(qhost,host) == 0)
{
ap->type = BADADDR;
addlist(&dlist,ap->val,0);
}
}
}
if(qhost != NULLCHAR)
{
#if defined (DEBUG_LOCKS)
smtplog ("mailit (): qhost = %s, queueing", qhost);
#endif
rewind(data);
queuejob(data,qhost,dlist,from,NULL);
del_list(dlist);
dlist = NULLLIST;
}
}
while(qhost != NULLCHAR)
;
for(ap = tolist;ap != NULLLIST;ap = ap->next)
{
if(ap->type != LOCAL)
{
ap->type = DOMAIN;
continue;
}
rewind(data);
/* strip off host name of LOCAL addresses */
if ((cp = strchr(ap->val,'@')) != NULLCHAR)
*cp = '\0';
/* truncate long user names */
if (strlen(ap->val) > MBOXLEN)
ap->val[MBOXLEN] = '\0';
/* if mail file is busy save it in our smtp queue
* and let the smtp daemon try later.
*/
#ifdef NOVELL
if(!novell_start)
{
#endif
if (mlock(Mailspool,ap->val))
{
int32 msgid;
#if defined (DEBUG_LOCKS)
smtplog ("mailit (): mail spool file %s, %s locked, queueing",
Mailspool, ap->val);
#endif
addlist(&dlist,ap->val,0);
fail = queuejob(data,Hostname,dlist,from,&msgid);
if (!fail)
delay_job(msgid);
del_list(dlist);
dlist = NULLLIST;
}
#ifdef NOVELL
}
#endif
else
{
char buf[LINELEN];
int tocnt = 0;
extern int smtpverbose;
#ifdef NOVELL
long int oid;
char box[13];
struct ffblk fblock;
static int done_rand=0;
if(novell_start)
{
oid=get_mail_id(ap->val,novell_server_name);
ltoa(oid,box,16);
if(!done_rand)
{
randomize(); /* This is non-deterministic */
done_rand=1;
}
do
{
sprintf(mailbox,"%s/%s/%s/%04x%04x.%s",novell_server_name,"SYS:/MAIL",box,rand(),rand(),novell_mail_ext);
}
while(!findfirst(mailbox,&fblock,0))
;
}
else
#endif
{
sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
}
#ifndef AMIGA
if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE)
{
# ifdef NOVELL
if(novell_start)
{
send_alert(ap->val,novell_server_name,from);
}
# endif
#else
if((fp = fopen(mailbox,"r+")) != NULLFILE)
{
(void) fseek(fp, 0L, 2);
#endif
time(&t);
if (smtp_separator)
fprintf(fp, "%s\n", smtp_separator);
fprintf(fp,"From %s %s",from,ctime(&t));
host = NULLCHAR;
while(fgets(buf,sizeof(buf),data) != NULLCHAR)
{
if(buf[0] == '\n')
{
if(tocnt == 0)
fprintf(fp,"%s%s\n",
Hdrs[APPARTO],
ap->val);
fputc('\n',fp);
break;
}
fputs(buf,fp);
rip(buf);
switch(htype(buf))
{
case TO:
case CC:
++tocnt;
break;
case RRECEIPT:
if((cp = getaddress(buf,0))
!= NULLCHAR)
{
free(host);
host = strdup(cp);
}
break;
}
}
copy_data(data, fp);
if(ferror(fp))
fail = 1;
else
fprintf(fp,"\n");
/* Leave a blank line between msgs */
fclose(fp);
if (smtpverbose)
{
tprintf("New mail arrived for %s from %s\n",ap->val, from);
}
if (Smtpbeep)
printf("\a\a");
if(host != NULLCHAR)
{
rewind(data); /* Send return receipt */
mdaemon(data,host,NULLLIST,0);
free(host);
}
}
else
fail = 1;
#ifdef NOVELL
if(!novell_start)
{
(void) rmlock(Mailspool,ap->val);
}
#else
(void) rmlock(Mailspool,ap->val);
#endif
if (fail)
{
break;
}
smtplog("deliver: To: %s From: %s",ap->val,from);
}
}
return fail;
}
/* Return Date/Time in Arpanet format in passed string */
char *
ptime(t)
long *t;
{
/* Print out the time and date field as
* "DAY day MONTH year hh:mm:ss ZONE"
*/
register struct tm *ltm;
static char tz[4];
static char str[40];
char *p;
#ifndef ATARI
*getenv();
#endif
/* Read the system time */
ltm = localtime(t);
if (*tz == '\0') {
if ((p = getenv("TZ")) == NULL)
strcpy(tz, "UTC");
else {
if (ltm->tm_isdst == 0)
strncpy(tz, p, 3);
else
strncpy(tz, p + 4, 3);
}
}
/* rfc 822 format */
sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
Days[ltm->tm_wday],
ltm->tm_mday,
Months[ltm->tm_mon],
ltm->tm_year,
ltm->tm_hour,
ltm->tm_min,
ltm->tm_sec,
tz);
return(str);
}
long
get_msgid()
{
char sfilename[LINELEN];
char s[20];
register long sequence = 0;
FILE *sfile;
sprintf(sfilename,"%s/sequence.seq",Mailqdir);
sfile = fopen(sfilename,READ_TEXT);
/* if sequence file exists, get the value, otherwise set it */
if (sfile != NULL)
{
(void) fgets(s,sizeof(s),sfile);
sequence = atol(s);
/* Keep it in range of and 8 digit number to use for dos name prefix. */
if (sequence < 0L || sequence > 99999999L )
sequence = 0;
fclose(sfile);
}
/* increment sequence number, and write to sequence file */
sfile = fopen(sfilename,WRITE_TEXT);
fprintf(sfile,"%ld",++sequence);
fclose(sfile);
return sequence;
}
#if defined(MSDOS) || defined(ATARI)
/* Illegal characters in a DOS filename */
static char baddoschars[] = "\"[]:|<>+=;,";
#endif
/* test if mail address is valid */
int
validate_address(s)
char *s;
{
char *cp, *t;
int32 addr;
/* if address has @ in it the check dest address */
if (*s == '@' && (cp = strrchr(s,':')) != NULLCHAR)
for (t = s,cp++; (*t++ = *cp++) != 0; )
;
if ((cp = strrchr(s,'@')) != NULLCHAR)
{
cp++;
/* 1st check if its our hostname
* if not then check the hosts file and see
* if we can resolve ther address to a know site
* or one of our aliases
*/
if (strcmp(cp,Hostname) != 0)
{
if ((addr = mailroute(cp, 0)) == 0
&& (Smtpmode & QUEUE) == 0)
return BADADDR;
if (ismyaddr(addr) == NULLIF)
return DOMAIN;
}
/* on a local address remove the host name part */
*--cp = '\0';
}
/* if using an external router leave address alone */
if ((Smtpmode & QUEUE) != 0)
return LOCAL;
/* check for the user%host hack */
if ((cp = strrchr(s,'%')) != NULLCHAR)
{
*cp = '@';
cp++;
/* reroute based on host name following the % seperator */
if (mailroute(cp, 0) == 0)
return BADADDR;
else
return DOMAIN;
}
#if defined (MSDOS) || defined(ATARI) /* dos file name checks */
/* Check for characters illegal in MS-DOS file names */
for(cp = baddoschars;*cp != '\0';cp++)
{
if(strchr(s,*cp) != NULLCHAR)
return BADADDR;
}
#endif
return LOCAL;
}
/* place a mail job in the outbound queue */
int
queuejob(dfile,host,to,from,msgid)
FILE *dfile;
char *host;
struct list *to;
char *from;
int32 *msgid;
{
FILE *fp;
struct list *ap;
char tmpstring[50], prefix[9], buf[LINELEN];
register int cnt;
int32 this_msgid;
this_msgid = get_msgid();
sprintf(prefix,"%ld",this_msgid);
if (msgid)
*msgid = this_msgid;
mlock(Mailqdir,prefix);
sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
{
(void) rmlock(Mailqdir,prefix);
return 1;
}
while((cnt = fread(buf, 1, LINELEN, dfile)) > 0)
if(fwrite(buf, 1, cnt, fp) != cnt)
break;
if(ferror(fp))
{
fclose(fp);
(void) rmlock(Mailqdir,prefix);
return 1;
}
fclose(fp);
sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
{
(void) rmlock(Mailqdir,prefix);
return 1;
}
fprintf(fp,"%s\n%s\n",host,from);
for(ap = to; ap != NULLLIST; ap = ap->next)
{
fprintf(fp,"%s\n",ap->val);
smtplog("queue job %s To: %s From: %s",prefix,ap->val,from);
}
fclose(fp);
(void) rmlock(Mailqdir,prefix);
return 0;
}
/* Deliver mail to the appropriate mail boxes */
static int
router_queue(data,from,to)
FILE *data;
char *from;
struct list *to;
{
register struct list *ap;
FILE *fp;
char tmpstring[50];
char prefix[9];
sprintf(prefix,"%ld",get_msgid());
mlock(Routeqdir,prefix);
sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
{
(void) rmlock(Routeqdir,prefix);
return 1;
}
rewind(data);
copy_data(data, fp);
if(ferror(fp))
{
fclose(fp);
(void) rmlock(Routeqdir,prefix);
return 1;
}
fclose(fp);
sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE)
{
(void) rmlock(Routeqdir,prefix);
return 1;
}
fprintf(fp,"From: %s\n",from);
for(ap = to;ap != NULLLIST;ap = ap->next)
{
fprintf(fp,"To: %s\n",ap->val);
}
fclose(fp);
(void) rmlock(Routeqdir,prefix);
smtplog("rqueue job %s From: %s",prefix,from);
return 0;
}
/* add an element to the front of the list pointed to by head
** return NULLLIST if out of memory.
*/
struct list *
addlist(head,val,type)
struct list **head;
char *val;
int type;
{
register struct list *tp;
tp = (struct list *)callocw(1,sizeof(struct list));
tp->next = NULLLIST;
/* allocate storage for the char string */
tp->val = strdup(val);
tp->type = type;
/* add entry to front of existing list */
if (*head == NULLLIST)
*head = tp;
else
{
tp->next = *head;
*head = tp;
}
return tp;
}
/* Remove and entry from the list ... CMS 19/12/92
return TRUE on success or FALSE on failure */
static int remlist(struct list **head, struct list *unwanted)
{
struct list *ptr, **prev;
prev = head;
for (ptr = *head; ptr; ptr = ptr->next)
if (ptr == unwanted)
{
*prev = ptr->next;
free (ptr);
break;
}
else prev = &ptr->next;
return ptr == unwanted;
}
#define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
#define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
/* check for and alias and expand alias into a address list */
static struct list *
expandalias(head, user)
struct list **head;
char *user;
{
FILE *fp;
register char *s,*p;
struct rr *rrp, *rrlp;
int inalias = 0;
struct list *tp;
char buf[LINELEN];
/* no alias file found */
if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE)
{
fail = FAIL_DELIVER;
/* Try MB, MG or MR domain name records */
rrlp = rrp = resolve_mailb(user);
while(rrp != NULLRR)
{
if(rrp->rdlength > 0)
{
/* remove the trailing dot */
rrp->rdata.name[rrp->rdlength-1] = '\0';
/* replace first dot with @ if there is no @ */
if(strchr(rrp->rdata.name,'@') == NULLCHAR
&& (p = strchr(rrp->rdata.name,'.')) !=
NULLCHAR)
*p = '@';
if(strchr(rrp->rdata.name,'@') != NULLCHAR)
tp = addlist(head,rrp->rdata.name,
DOMAIN);
else
tp = addlist(head,rrp->rdata.name,
LOCAL);
++inalias;
}
rrp = rrp->next;
}
free_rr(rrlp);
if(inalias)
return tp;
else
return addlist(head, user, LOCAL);
}
while (fgets(buf,LINELEN,fp) != NULLCHAR)
{
p = buf;
if ( *p == '#' || *p == '\0')
continue;
rip(p);
/* if not in an matching entry skip continuation lines */
if (!inalias && isspace(*p))
continue;
/* when processing an active alias check for a continuation */
if (inalias)
{
if (!isspace(*p))
break; /* done */
}
else
{
s = p;
SKIPWORD(p);
*p++ = '\0'; /* end the alias name */
SKIPSPACE (p);
if (fail == NO_FAIL && strcmp(s, "default") == 0)
if (strncmp(p, "deliver", 3) == 0)
fail = FAIL_DELIVER;
else {
fail = FAIL_DEFAULT;
strcpy(default_address, p);
}
if (strcmp(s,user) != 0)
continue; /* no match go on */
inalias = 1;
}
/* process the recipients on the alias line */
SKIPSPACE(p);
while(*p != '\0' && *p != '#')
{
s = p;
SKIPWORD(p);
if (*p != '\0')
*p++ = '\0';
/* find hostname */
if (strchr(s,'@') != NULLCHAR)
tp = addlist(head,s,DOMAIN);
else
tp = addlist(head,s,LOCAL);
SKIPSPACE(p);
}
}
(void) fclose(fp);
if (inalias) /* found and processed and alias. */
return tp;
/* no alias found treat as a local address */
return addlist(head, user, LOCAL);
}
#if defined(ANSIPROTO)
static void
smtplog(char *fmt, ...)
{
va_list ap;
char *cp;
long t;
FILE *fp;
if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
return;
time(&t);
cp = ctime(&t);
rip(cp);
fprintf(fp,"%s ",cp);
va_start(ap,fmt);
vfprintf(fp,fmt,ap);
va_end(ap);
fprintf(fp,"\n");
fclose(fp);
}
#else
static void
smtplog(fmt,arg1,arg2,arg3,arg4)
char *fmt;
int arg1,arg2,arg3,arg4;
{
char *cp;
long t;
FILE *fp;
if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
return;
time(&t);
cp = ctime(&t);
rip(cp);
fprintf(fp,"%s ",cp);
fprintf(fp,fmt,arg1,arg2,arg3,arg4);
fprintf(fp,"\n");
fclose(fp);
}
#endif
/* send mail to a single user. Can be called from the ax24 mailbox or
** from the return mail function in the smtp client
*/
static int
mailuser(data,from,to)
FILE *data;
char *from;
char *to;
{
int address_type, ret;
struct list *tolist = NULLLIST;
/* check if address is ok */
if ((address_type = validate_address(to)) == BADADDR)
{
return 1;
}
/* if a local address check for an alias */
if (address_type == LOCAL)
expandalias(&tolist, to);
else
/* a remote address is added to the list */
addlist(&tolist, to, address_type);
ret = mailit(data,from,tolist);
del_list(tolist);
return ret;
}
/* Mailer daemon return mail mechanism */
int
mdaemon(data,to,lp,bounce)
FILE *data; /* pointer to rewound data file */
char *to; /* Overridden by Errors-To: line if bounce is true */
struct list *lp; /* error log for failed mail */
int bounce; /* True for failed mail, otherwise return receipt */
{
time_t t;
FILE *tfile;
char buf[LINELEN], *cp, *newto = NULLCHAR;
int cnt;
if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce)
{
while(fgets(buf,sizeof(buf),data) != NULLCHAR)
{
if(buf[0] == '\n')
break;
/* Look for Errors-To: */
if(htype(buf) == ERRORSTO &&
(cp = getaddress(buf,0)) != NULLCHAR)
{
free(newto);
newto = strdup(cp);
break;
}
}
if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') ||
to == NULLCHAR))
return -1;
rewind(data);
}
if((tfile = tmpfile()) == NULLFILE)
return -1;
time(&t);
fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t));
fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
Hdrs[FROM],Hostname);
fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to);
fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT],
bounce ? "Failed mail" : "Return receipt");
if(bounce)
{
fprintf(tfile," ===== transcript follows =====\n\n");
for (; lp != NULLLIST; lp = lp->next)
fprintf(tfile,"%s\n",lp->val);
fprintf(tfile,"\n");
}
fprintf(tfile," ===== %s follows ====\n",
bounce ? "Unsent message" : "Message header");
while(fgets(buf,sizeof(buf),data) != NULLCHAR)
{
if(buf[0] == '\n')
break;
fputs(buf,tfile);
}
if(bounce)
{
fputc('\n',tfile);
while((cnt = fread(buf,1,sizeof(buf),data)) > 0)
fwrite(buf,1,cnt,tfile);
}
fseek(tfile,0L,0);
/* A null From<> so no looping replys to MAIL-DAEMONS */
sprintf(buf, "postmaster@%s", Hostname);
mailuser(tfile, buf, newto != NULLCHAR ? newto : to);
fclose(tfile);
free(newto);
return 0;
}
/* Check to see that we are accepting mail from this sender. If we aren't
* then tell 'em that mail from them is Unwanted. I added this facility to
* allow auto-rejection of messages from auto-reply daemons. G7LEU
*/
static int
validate_sender(sender)
char *sender;
{
FILE *fp;
char buf[LINELEN + 1], *p;
if ((fp = fopen(Mailkill, READ_TEXT)) == NULLFILE)
/* Can't open SMTP reject file so let 'em through */
return 1;
while (fgets(buf,LINELEN,fp) != NULLCHAR)
{
p = buf;
if (*p == '#' || *p == '\0')
continue;
rip(p);
if (!stricmp(p, sender))
{
extern int smtpverbose;
if (smtpverbose)
tprintf("SMTP: Rejected mail from '%s'\n", sender);
fclose(fp);
return 0;
}
}
fclose(fp);
/* Sender OK */
return 1;
}
/* Check to see if a local user actually exists - use the FTPUSERS file
* Returns 0 if the user doesn't exist, 1 otherwise
*/
static int
validate_user(user)
char *user;
{
char *cp;
/* always allow 'postmaster' */
if (stricmp(user,"postmaster")==0)
{
return 1;
}
/* otherwise check the ftpusers file */
# ifdef NOVELL
if(novell_start)
{
if(!get_mail_id(user,novell_server_name))
{
return 0;
}
}
else
# endif
{
if ((cp=userlookup(user,NULL,NULLCHARP,NULLINT,NULL)) == NULLCHAR)
{
return 0;
}
free(cp);
}
return 1;
}